home *** CD-ROM | disk | FTP | other *** search
/ Light ROM 1 / LIGHT-ROM 1 (Amiga Library Services)(1994).iso / ffdisks / d985.lha / NewIFF / NewIFF39.lha / newiff39 / apps / PlaySMUS / PlaySMUS.c < prev    next >
C/C++ Source or Header  |  1993-09-28  |  29KB  |  1,027 lines

  1.  
  2. /** PlaySMUS.c ************************************************************** 
  3.  * 
  4.  * Read (and someday play) a SMUS file - in progress
  5.  * 
  6.  * requires linkage with several IFF modules - see Makefile
  7.  ****************************************************************************/ 
  8.  
  9. #include "iffp/8svxapp.h"
  10. #include "iffp/smusapp.h"
  11.  
  12. #include <exec/execbase.h>
  13. #include <graphics/gfxbase.h>
  14. #include <clib/alib_protos.h>
  15.  
  16.  
  17. #ifdef __SASC
  18. void __chkabort(void) {}          /* Disable SAS CTRL-C checking. */
  19. #else
  20. #ifdef LATTICE
  21. void chkabort(void) {}            /* Disable LATTICE CTRL-C checking */
  22. #endif
  23. #endif
  24.  
  25.  
  26. #define MINARGS 2
  27.  
  28. #include "playsmus_rev.h"
  29. UBYTE vers[] = VERSTAG;
  30. UBYTE Copyright[] = VERS " - SMUS loader (no play yet) - Freely Redistributable";
  31.  
  32. char *usage = "Usage: PlaySMUS SMUSname [instrument path]";
  33.  
  34. /* prototypes for our functions */
  35. void cleanup(void);
  36. void bye(UBYTE *s,int error);
  37. void DUnpack(BYTE source[], LONG n, BYTE dest[]);
  38. BYTE D1Unpack(BYTE source[], LONG n, BYTE dest[], BYTE x);
  39. LONG LoadSample(struct EightSVXInfo *esvx, UBYTE *filename);
  40. void UnloadSample(struct EightSVXInfo *esvx);
  41. LONG LoadSBody(struct EightSVXInfo *esvx);
  42. void UnloadSBody(struct EightSVXInfo *esvx);
  43. LONG ShowSample(struct EightSVXInfo *esvx);
  44.  
  45. LONG LoadSMUS(struct SMUSInfo *smus, UBYTE *filename);
  46. void UnloadSMUS(struct SMUSInfo *esvx);
  47.  
  48. LONG OpenAudio(void);
  49. void CloseAudio(void);
  50. LONG PlaySample(struct EightSVXInfo *esvx,
  51.         LONG octave, LONG note, UWORD volume, ULONG delay);
  52.  
  53. char *inspath = "Instruments:";
  54.  
  55. /* globals */
  56. struct Library *IFFParseBase   = NULL;
  57. struct Library *GfxBase = NULL;
  58.  
  59. BOOL   FromWb;
  60.  
  61. /* SMUS Property chunks to be grabbed
  62.  */
  63. LONG    smusprops[] = {
  64.         ID_SMUS, ID_SHDR,
  65.         ID_SMUS, ID_NAME,
  66.         ID_SMUS, ID_AUTH,
  67.         ID_SMUS, ID_Copyright,
  68.         TAG_DONE
  69.         };
  70.  
  71. /* SMUS Collection chunks (more than one in file) to be gathered */
  72. LONG    smuscollects[] = {
  73.         ID_SMUS, ID_ANNO,
  74.         TAG_DONE
  75.         };
  76.  
  77. /* SMUS Chunks to stop on */
  78. LONG    smusstops[] = {
  79.         ID_SMUS, ID_TRAK,
  80.         ID_SMUS, ID_INS1,
  81.         TAG_DONE
  82.         };
  83.  
  84.  
  85. /* 8SVX Property chunks to be grabbed
  86.  */
  87. LONG    esvxprops[] = {
  88.         ID_8SVX, ID_VHDR,
  89.         ID_8SVX, ID_NAME,
  90.         ID_8SVX, ID_ATAK,
  91.         ID_8SVX, ID_RLSE,
  92.         ID_8SVX, ID_AUTH,
  93.         ID_8SVX, ID_Copyright,
  94.         TAG_DONE
  95.         };
  96.  
  97. /* 8SVX Collection chunks (more than one in file) to be gathered */
  98. LONG    esvxcollects[] = {
  99.         ID_8SVX, ID_ANNO,
  100.         TAG_DONE
  101.         };
  102.  
  103. /* 8SVX Chunk to stop on */
  104. LONG    esvxstops[] = {
  105.         ID_8SVX, ID_BODY,
  106.         TAG_DONE
  107.         };
  108.  
  109.  
  110. UBYTE nomem[]  = "Not enough memory\n";
  111. UBYTE noiffh[] = "Can't alloc iff\n";
  112.  
  113.  
  114.  
  115. /* For our allocated SMUSInfo */
  116. struct SMUSInfo *smus = NULL;
  117.  
  118. /* For allocated 8SVX Infos */
  119. struct EightSVXInfo  *esvxs[MAXINS] = { 0 };
  120. struct EightSVXInfo  *esvx = NULL;
  121. ULONG  ei = 0, ti = 0, tcnt = 0, ecnt = 0;
  122.  
  123.  
  124. /* 
  125.  * MAIN 
  126.  */
  127. void main(int argc, char **argv)
  128.    {
  129.    UBYTE *smusname=NULL, *esvxname=NULL;
  130.    ULONG oct;
  131.    LONG error=0L;
  132.  
  133.    FromWb = argc ? FALSE : TRUE;
  134.  
  135.    if((argc<MINARGS)||(argv[argc-1][0]=='?'))
  136.     {
  137.     printf("%s\n%s\n",Copyright,usage);
  138.         bye("",RETURN_OK);
  139.     }
  140.  
  141.    smusname = argv[1];
  142.    if(argc > 2)  inspath = argv[2];
  143.  
  144. /* Open Libraries */
  145.    if(!(IFFParseBase = OpenLibrary("iffparse.library",0)))
  146.       bye("Can't open iffparse library.\n",RETURN_WARN);
  147.  
  148.  
  149. /* 
  150.  * Alloc one SMUSInfo struct
  151.  */
  152.     if(!(smus = (struct SMUSInfo *)
  153.     AllocMem(sizeof(struct SMUSInfo),MEMF_PUBLIC|MEMF_CLEAR))) 
  154.         bye(nomem,RETURN_FAIL);
  155.  
  156. /*
  157.  * Here we set up our SMUSInfo fields for our application.
  158.  * Above we have defined the propery and collection chunks
  159.  * we are interested in.
  160.  * We want to stop on INS1 or TRAK.
  161.  */
  162.     smus->ParseInfo.propchks    = smusprops;
  163.     smus->ParseInfo.collectchks    = smuscollects;
  164.     smus->ParseInfo.stopchks    = smusstops;
  165. /* 
  166.  * Alloc the IFF handle for the frame
  167.  */
  168.     if(!(smus->ParseInfo.iff = AllocIFF())) bye(noiffh,RETURN_FAIL);
  169.  
  170.  
  171. /* 
  172.  * Alloc one EightSVXInfo struct for defaults
  173.  */
  174.     if(!(esvx = (struct EightSVXInfo *)
  175.     AllocMem(sizeof(struct EightSVXInfo),MEMF_PUBLIC|MEMF_CLEAR))) 
  176.         bye(nomem,RETURN_FAIL);
  177.  
  178. /*
  179.  * Here we set up default EightSVXInfo fields for our
  180.  * application.
  181.  * Above we have defined the propery and collection chunks
  182.  * we are interested in (some required like VHDR).
  183.  * We want to stop on BODY.
  184.  */
  185.     esvx->ParseInfo.propchks    = esvxprops;
  186.     esvx->ParseInfo.collectchks    = esvxcollects;
  187.     esvx->ParseInfo.stopchks    = esvxstops;
  188. /* 
  189.  * Alloc the IFF handle for the frame
  190.  */
  191.     if(!(esvx->ParseInfo.iff = AllocIFF())) bye(noiffh,RETURN_FAIL);
  192.  
  193.     if(error = LoadSMUS(smus, smusname))
  194.     {
  195.     printf("Error %ld: %s\n",error, IFFerr(error));
  196.     goto done;
  197.     }
  198. goto done;
  199.  
  200.     if(!(error = LoadSample(esvx, esvxname)))
  201.     {
  202.     ShowSample(esvx);
  203.  
  204.     if(!(error = OpenAudio()))
  205.         {
  206.         /* If we think this is a sound effect, play it as such (note=-1) */
  207.         if((esvx->Vhdr.ctOctave==1)&&(esvx->Vhdr.samplesPerSec)
  208.         &&(esvx->Vhdr.oneShotHiSamples)&&(!esvx->Vhdr.repeatHiSamples))
  209.         {
  210.         PlaySample(esvx,0,-1,64,0);
  211.         }
  212.         /* Else play it like an instrument */
  213.         else
  214.         {
  215.             for(oct=0; oct < esvx->Vhdr.ctOctave; oct++)
  216.             {
  217.                 PlaySample(esvx,oct,0,64,50);
  218.                 PlaySample(esvx,oct,4,64,50);
  219.                 PlaySample(esvx,oct,7,64,50);
  220.             }
  221.         }
  222.         CloseAudio();
  223.         }
  224.         else printf("error opening audio device\n");
  225.     }
  226.     else
  227.         printf("%s\n",IFFerr(error));
  228.  
  229. done:
  230.     D(bug("done\n"));
  231.     cleanup();
  232.     exit(RETURN_OK);
  233.     }
  234.  
  235.  
  236. void bye(UBYTE *s,int error)
  237.    {
  238.    if((*s)&&(!FromWb)) printf("%s\n",s);
  239.    cleanup();
  240.    exit(error);
  241.    }
  242.  
  243.  
  244. void cleanup()
  245.    {
  246.    int k;
  247.  
  248.    D(bug("cleanup:\n"));
  249.  
  250.    if(esvx)        
  251.     {
  252. /*
  253.     DD(bug("About to UnloadSample\n"));
  254.     UnloadSample(esvx);
  255. */
  256.     if(esvx->ParseInfo.iff)     FreeIFF(esvx->ParseInfo.iff);
  257.         FreeMem(esvx,sizeof(struct EightSVXInfo));
  258.     esvx = NULL;
  259.     }
  260.  
  261.    for(k = 0; (k <MAXINS) && (esvxs[k]) ; k++)
  262.     {
  263.     if(esvxs[k])
  264.         {
  265.         D(bug("cleanup: about to UnloadSample %ld\n",k));
  266.         UnloadSample(esvxs[k]);
  267.         D(bug("cleanup: about to FreeIFF %ld\n",k));
  268.         if(esvxs[k]->ParseInfo.iff)     FreeIFF(esvxs[k]->ParseInfo.iff);
  269.             FreeMem(esvxs[k],sizeof(struct EightSVXInfo));
  270.         esvxs[k] = NULL;
  271.         }
  272.     }
  273.  
  274.    if(smus)
  275.     {
  276.     DD(bug("About to UnloadSMUS\n"));
  277.     UnloadSMUS(smus);
  278.     if(smus->ParseInfo.iff)     FreeIFF(smus->ParseInfo.iff);
  279.     FreeMem(smus,sizeof(struct SMUSInfo));
  280.     smus = NULL;
  281.     }
  282.  
  283.    if(IFFParseBase)      CloseLibrary(IFFParseBase);
  284.    }
  285.  
  286.  
  287. /** ShowSample() **********************************************
  288.  * 
  289.  * Show sample information after calling LoadSample()
  290.  * 
  291.  *************************************************************************/
  292. LONG ShowSample(struct EightSVXInfo *esvx)
  293.     {
  294.     LONG error = 0L;
  295.     BYTE *buf;
  296.     Voice8Header *vhdr;
  297.  
  298.     if(!esvx)            return(CLIENT_ERROR);
  299.     if(!(buf = esvx->sample))    return(CLIENT_ERROR);
  300.  
  301.     /* LoadSample copied VHDR and NAME (if any) to our esvx frame */
  302.     vhdr = &esvx->Vhdr;
  303.     if(esvx->name[0]) printf("\nNAME: %s",esvx->name);
  304.  
  305.     printf("\n\nVHDR Info:");
  306.     printf("\noneShotHiSamples=%ld", vhdr->oneShotHiSamples); 
  307.     printf("\nrepeatHiSamples=%ld", vhdr->repeatHiSamples); 
  308.     printf("\nsamplesPerHiCycle=%ld", vhdr->samplesPerHiCycle); 
  309.     printf("\nsamplesPerSec=%ld", vhdr->samplesPerSec); 
  310.     printf("\nctOctave=%ld", vhdr->ctOctave); 
  311.     printf("\nsCompression=%ld", vhdr->sCompression); 
  312.     printf("\nvolume=0x%lx", vhdr->volume); 
  313.     printf("\nData = %3ld %3ld %3ld %3ld %3ld %3ld %3ld %3ld",  
  314.            buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); 
  315.     printf("\n       %3ld %3ld %3ld %3ld %3ld %3ld %3ld %3ld ...\n",  
  316.            buf[8+0],buf[8+1],buf[8+2],buf[8+3],buf[8+4],buf[8+5],
  317.            buf[8+6],buf[8+ 7]); 
  318.  
  319.     return(error);
  320.     } 
  321.  
  322.  
  323. /* OpenAudio
  324.  *
  325.  * Opens audio device for one audio channel, 2 IO requests
  326.  * Returns 0 for success
  327.  *
  328.  * Based on code by Dan Baker
  329.  */
  330.  
  331. UBYTE           whichannel[] = { 1,2,4,8 };
  332.  
  333. /* periods for scale starting at   65.40Hz (C) with 128 samples per cycle
  334.  *                            or  130.81Hz (C) with  64 samples per cycle
  335.  *                            or  261.63Hz (C) with  32 samples per cycle
  336.  *                            or  523.25Hz (C) with  16 samples per cycle
  337.  *                            or 1046.50Hz (C) with   8 samples per cycle
  338.  *                            or 2093.00Hz (C) with   4 samples per cycle
  339.  */
  340.  
  341. UWORD   per_ntsc[12]= { 428, 404, 380, 360,
  342.             340, 320, 302, 286,
  343.             270, 254, 240, 226 };
  344.  
  345. /* periods adjusted for system clock frequency */
  346. UWORD   per[12];
  347.  
  348. /* Note - these values 3579545 NTSC, 3546895 PAL */
  349. #define NTSC_CLOCK 3579545L
  350. #define PAL_CLOCK  3546895L
  351.  
  352. #define AIOCNT 4
  353. struct     IOAudio *aio[AIOCNT] = {NULL};       /* Ptrs to IO blocks for commands  */
  354.  
  355. struct     MsgPort *port;       /* Pointer to a port so the device can reply */
  356. BOOL    devopened;
  357. ULONG    clock = NTSC_CLOCK;     /* Will check for PAL and change if necessary */
  358.  
  359.  
  360. LONG OpenAudio()
  361. {
  362. extern    struct ExecBase *SysBase;
  363. LONG     error=0L;
  364. ULONG   period;
  365. int    k;
  366.  
  367. if(devopened)    return(-1);
  368.  
  369. /*-------------------------------------------------------------------------*/
  370. /* Ask the system if we are PAL or NTSC and set clock constant accordingly */
  371. /*-------------------------------------------------------------------------*/
  372. if(GfxBase=OpenLibrary("graphics.library",0L))
  373.     {
  374.     if(((struct GfxBase *)GfxBase)->DisplayFlags & PAL)
  375.         clock = PAL_CLOCK;
  376.     else
  377.         clock = NTSC_CLOCK;
  378.     CloseLibrary((struct Library *) GfxBase);
  379.     }
  380.  
  381. printf("OpenAudio: For period calculations, clock=%ld\n", clock);
  382.  
  383. /* calculate period values for one octave based on system clock */
  384. for(k=0; k<12; k++)
  385.     {
  386.     period = ((per_ntsc[k] * clock) + (NTSC_CLOCK >> 1)) / NTSC_CLOCK;
  387.     per[k] = period;
  388.     D(bug("per[%ld]=%ld ",k,per[k]));
  389.     }
  390. D(bug("\n"));
  391.  
  392. /*-------------------------------------------------------------------*/
  393. /* Create a reply port so the audio device can reply to our commands */
  394. /*-------------------------------------------------------------------*/
  395. if(!(port=CreatePort(0,0)))
  396.     { error = 1; goto bailout; }
  397.  
  398. /*--------------------------------------------------------------------------*/
  399. /*  Create audio I/O blocks so we can send commands to the audio device     */
  400. /*--------------------------------------------------------------------------*/
  401. for(k=0; k<AIOCNT; k++)
  402.     {
  403.     if(!(aio[k]=(struct IOAudio *)CreateExtIO(port,sizeof(struct IOAudio))))
  404.     { error = k+2; goto bailout; }
  405.     }
  406.  
  407. /*----------------------------------------------------------------------*/
  408. /* Set up the audio I/O block for channel allocation:                   */
  409. /* ioa_Request.io_Message.mn_ReplyPort is the address of a reply port.  */
  410. /* ioa_Request.io_Message.mn_Node.ln_Pri sets the precedence (priority) */
  411. /*   of our use of the audio device. Any tasks asking to use the audio  */
  412. /*   device that have a higher precedence will steal the channel from us.*/
  413. /* ioa_Request.io_Command is the command field for IO.                  */
  414. /* ioa_Request.io_Flags is used for the IO flags.                       */
  415. /* ioa_AllocKey will be filled in by the audio device if the allocation */
  416. /*   succeeds. We must use the key it gives for all other commands sent.*/
  417. /* ioa_Data is a pointer to the array listing the channels we want.     */
  418. /* ioa_Length tells how long our list of channels is.                   */
  419. /*----------------------------------------------------------------------*/
  420. aio[0]->ioa_Request.io_Command               = ADCMD_ALLOCATE;
  421. aio[0]->ioa_Request.io_Flags                 = ADIOF_NOWAIT;
  422. aio[0]->ioa_AllocKey                         = 0;
  423. aio[0]->ioa_Data                             = whichannel;
  424. aio[0]->ioa_Length                           = sizeof(whichannel);
  425.  
  426. /*-----------------------------------------------*/
  427. /* Open the audio device and allocate a channel  */
  428. /*-----------------------------------------------*/
  429. if(!(OpenDevice("audio.device",0L, (struct IORequest *) aio[0] ,0L)))
  430.     devopened = TRUE;
  431. else { error = 5; goto bailout; }
  432.  
  433. /* Clone the flags, channel allocation, etc. into other IOAudio requests */
  434. for(k=1; k<AIOCNT; k++)    *aio[k] = *aio[0];
  435.  
  436. bailout:
  437. if(error)    
  438.     {
  439.     printf("OpenAudio errored out at step %ld\n",error);
  440.     CloseAudio();
  441.     }
  442. return(error);
  443. }
  444.  
  445.  
  446. /* CloseAudio
  447.  *
  448.  * Close audio device as opened by OpenAudio, null out pointers
  449.  */
  450. void CloseAudio()
  451. {
  452. int k;
  453.  
  454. D(bug("Closing audio device...\n"));
  455.  
  456. /* Note - we know we have no outstanding audio requests */
  457. if(devopened)
  458.     {
  459.     CloseDevice((struct IORequest *) aio[0]);
  460.     devopened = FALSE;
  461.     }
  462.  
  463. for(k=0; k<AIOCNT; k++)
  464.     {
  465.     if(aio[k])     DeleteExtIO(aio[k]), aio[k] = NULL;
  466.     }
  467.  
  468. if(port)       DeletePort(port),  port = NULL;
  469. }
  470.  
  471.  
  472. /** PlaySample() **********************************************
  473.  * 
  474.  * Play a note in octave for delay/50ths of a second 
  475.  * OR Play a sound effect (set octave and note to 0, -1)
  476.  *
  477.  * Requires successful OpenAudio() called previously
  478.  *
  479.  * When playing notes:
  480.  * Expects note values between 0 (C) and 11 (B#)
  481.  * Uses largest octave sample in 8SVX as octave 0, next smallest
  482.  *   as octave 1, etc.
  483.  *
  484.  * Notes - this simple example routine does not do ATAK and RLSE)
  485.  *       - use of Delay for timing is simplistic, synchronous, and does
  486.  *        not take into account that the oneshot itself may be
  487.  *        longer than the delay.
  488.  *         Use timer.device for more accurate asynchronous delays
  489.  *
  490.  *************************************************************************/
  491. /* Max playable sample in one IO request is 128K */
  492. #define MAXSAMPLE 131072
  493.  
  494. LONG    PlaySample(struct EightSVXInfo *esvx,
  495.             LONG octave, LONG note, UWORD volume, ULONG delay)
  496. {
  497. /* pointers to outstanding requests */
  498. struct        IOAudio    *aout0=NULL, *aout1=NULL;    
  499. ULONG        period;
  500. LONG        osize, rsize;
  501. BYTE        *oneshot, *repeat;
  502.  
  503. if(!devopened)    return(-1);
  504.  
  505. if(note > 11) note=0;
  506.  
  507. if( note == -1 ) period = clock / esvx->Vhdr.samplesPerSec;
  508. else          period = per[note]; /* table set up by OpenAudio */
  509.  
  510. if(octave > esvx->Vhdr.ctOctave) octave = 0;
  511. if(volume > 64)    volume = 64;
  512.  
  513. oneshot = esvx->osamps[octave];
  514. osize   = esvx->osizes[octave];
  515. repeat  = esvx->rsamps[octave];
  516. rsize   = esvx->rsizes[octave];
  517.  
  518. D(bug("oneshot $%lx size %ld, repeat $%lx size %ld\n",
  519.     oneshot, osize, repeat, rsize));
  520.  
  521. /*------------------------------------------------------------*/
  522. /* Set up audio I/O blocks to play a sample using CMD_WRITE.  */
  523. /* Set up one request for the oneshot and one for repeat      */
  524. /* (all ready for simple case, but we may not need both)      */
  525. /* The io_Flags are set to ADIOF_PERVOL so we can set the     */
  526. /*    period (speed) and volume with the our sample;          */
  527. /* ioa_Data points to the sample; ioa_Length gives the length */
  528. /* ioa_Cycles tells how many times to repeat the sample       */
  529. /* If you want to play the sample at a given sampling rate,   */
  530. /* set ioa_Period = clock/(given sampling rate)               */
  531. /*------------------------------------------------------------*/
  532. aio[0]->ioa_Request.io_Command             =CMD_WRITE;
  533. aio[0]->ioa_Request.io_Flags               =ADIOF_PERVOL;
  534. aio[0]->ioa_Data                           =oneshot;
  535. aio[0]->ioa_Length                         =osize;
  536. aio[0]->ioa_Period                         =period;
  537. aio[0]->ioa_Volume                         =volume;
  538. aio[0]->ioa_Cycles                         =1;
  539.  
  540. aio[2]->ioa_Request.io_Command             =CMD_WRITE;
  541. aio[2]->ioa_Request.io_Flags               =ADIOF_PERVOL;
  542. aio[2]->ioa_Data                           =repeat;
  543. aio[2]->ioa_Length                         =rsize;
  544. aio[2]->ioa_Period                         =period;
  545. aio[2]->ioa_Volume                         =volume;
  546. aio[2]->ioa_Cycles                         =0;    /* repeat until stopped */
  547.  
  548. /*---------------------------------------------------*/
  549. /* Send the command to start a sound using BeginIO() */
  550. /* Go to sleep and wait for the sound to finish with */
  551. /* WaitIO() to wait and get the get the ReplyMsg     */
  552. /*---------------------------------------------------*/
  553. printf("Starting tone O len %ld for %0ld cyc, R len %ld for %0ld cyc, per=%ld...",
  554.         osize, aio[0]->ioa_Cycles, rsize, aio[1]->ioa_Cycles, period);
  555.  
  556. if(osize)
  557.     {
  558.     /* Simple case for oneshot sample <= 128K (ie. most samples) */
  559.     if(osize <= MAXSAMPLE)    BeginIO((struct IORequest *)(aout0=aio[0]));
  560.      }
  561.  
  562. if(rsize)
  563.     {
  564.     /* Simple case for oneshot sample <= 128K (ie. most samples) */
  565.     if(rsize <= MAXSAMPLE)    BeginIO((struct IORequest *)(aout1=aio[2]));
  566.     }
  567.  
  568. if(delay)    Delay(delay);    /* crude timing for notes */
  569.  
  570. /* Wait for any requests we still have out */
  571. if(aout0) WaitIO(aout0);
  572.  
  573. if(aout1)
  574.    {
  575.    if(note >= 0) AbortIO(aout1);    /* if a note, stop it now */
  576.    WaitIO(aout1);
  577.    }
  578.  
  579. printf("Done\n");
  580. }
  581.  
  582.  
  583. /** LoadSample() **********************************************************
  584.  * 
  585.  * Read 8SVX, given an initialized EightSVXInfo with not-in-use IFFHandle,
  586.  *   and filename.  Leaves the IFFHandle open so you can FindProp()
  587.  *   additional chunks or copychunks().  You must UnloadSample()
  588.  *   when done.  UnloadSample will closeifile if the file is still
  589.  *   open.
  590.  *
  591.  * Fills in esvx->Vhdr and Name, and allocates/loads esvx->sample,
  592.  *   setting esvx->samplebytes to size for deallocation.
  593.  *
  594.  * Returns 0 for success of an IFFERR (libraries/iffparse.h)
  595.  *************************************************************************/
  596. LONG LoadSample(struct EightSVXInfo *esvx, UBYTE *filename)
  597.     {
  598.     struct IFFHandle *iff;
  599.     struct StoredProperty *sp;
  600.     Voice8Header *vhdr;
  601.     BYTE *oneshot, *repeat;
  602.     ULONG osize, rsize, spcyc;
  603.     int oct;
  604.     LONG error = 0L;
  605.  
  606.     D(bug("LoadSample: looking for %s\n",filename));
  607.     
  608.     if(!esvx)                return(CLIENT_ERROR);
  609.     if(!(iff=esvx->ParseInfo.iff))    return(CLIENT_ERROR);
  610.  
  611.     if(!(error = openifile((struct ParseInfo *)esvx, filename, IFFF_READ)))
  612.     {
  613.     printf("Reading '%s'...\n",filename);
  614.     error = parseifile((struct ParseInfo *)esvx,
  615.             ID_FORM, ID_8SVX,
  616.             esvx->ParseInfo.propchks,
  617.             esvx->ParseInfo.collectchks,
  618.             esvx->ParseInfo.stopchks);
  619.  
  620.     D(bug("LoadSample: after parseifile - error = %ld\n",error));
  621.  
  622.     if((!error)||(error == IFFERR_EOC)||(error == IFFERR_EOF))
  623.         {
  624.         if(contextis(iff,ID_8SVX,ID_FORM))
  625.         {
  626.         D(bug("LoadSample: context is 8SVX\n"));
  627.         if(!(sp = FindProp(iff,ID_8SVX,ID_VHDR)))
  628.             {
  629.             message("No 8SVX.VHDR!");
  630.             error = IFFERR_SYNTAX;
  631.             }
  632.         else
  633.             {
  634.             D(bug("LoadSample: Have VHDR\n"));
  635.             /* copy Voice8Header into frame */
  636.             vhdr = (Voice8Header *)(sp->sp_Data);
  637.             *(&esvx->Vhdr) = *vhdr;
  638.             /* copy name if any */
  639.             esvx->name[0]='\0';
  640.             if(sp = FindProp(iff,ID_8SVX,ID_NAME))
  641.             {
  642.             strncpy(esvx->name,sp->sp_Data,sp->sp_Size);
  643.             esvx->name[MIN(sp->sp_Size,79)] = '\0';
  644.             }
  645.                 error = LoadSBody(esvx);
  646.             D(bug("LoadSample: After LoadSBody - error = %ld\n",error));
  647.             if(!error)
  648.             {
  649.             osize   = esvx->Vhdr.oneShotHiSamples;
  650.             rsize   = esvx->Vhdr.repeatHiSamples;
  651.             spcyc    = esvx->Vhdr.samplesPerHiCycle;
  652.             if(!spcyc) spcyc = esvx->Vhdr.repeatHiSamples;
  653.             if(!spcyc) spcyc = 8;
  654.  
  655.             oneshot = esvx->sample;
  656.  
  657.             for(oct = esvx->Vhdr.ctOctave-1; oct >= 0;
  658.                  oct--, oneshot+=(osize+rsize),
  659.                     osize <<= 1, rsize <<=1, spcyc <<=1)
  660.                     {
  661.                     repeat  = oneshot + osize;
  662.                 esvx->osizes[oct] = osize;
  663.                 if(osize) esvx->osamps[oct] = oneshot;
  664.                 else      esvx->osamps[oct] = 0;
  665.                 esvx->rsizes[oct] = rsize;
  666.                 if(rsize) esvx->rsamps[oct] = repeat;
  667.                 else      esvx->rsamps[oct] = 0;
  668.                 esvx->spcycs[oct] = spcyc;
  669.  
  670.              D(bug("oneshot $%lx size %ld, repeat $%lx size %ld\n",
  671.                 oneshot, osize, repeat, rsize));
  672.  
  673.                 }
  674.                 }
  675.             }
  676.         }
  677.         else
  678.         {
  679.         message("Not an 8SVX\n");
  680.         error = NOFILE;
  681.         }
  682.         }
  683.     }
  684.  
  685.     if(error)
  686.     {
  687.     closeifile((struct ParseInfo *)esvx);
  688.     UnloadSample(esvx);
  689.     }
  690.     return(error);
  691.     }
  692.  
  693.  
  694. /** UnloadSample() *******************************************************
  695.  * 
  696.  * Frees and closes everything opened/alloc'd by LoadSample()
  697.  *
  698.  *************************************************************************/
  699. void UnloadSample(struct EightSVXInfo *esvx)
  700.     {
  701.     if(esvx)
  702.     {
  703.     D(bug("UnloadSample: About to UnloadSBody\n"));
  704.     UnloadSBody(esvx);
  705.     D(bug("UnloadSample: About to closeifile\n"));
  706.     closeifile((struct ParseInfo *)esvx);
  707.     }
  708.     }
  709.  
  710.  
  711. /** LoadSBody() ***********************************************************
  712.  * 
  713.  * Read a 8SVX Sample BODY into RAM.  
  714.  * 
  715.  *************************************************************************/
  716. LONG LoadSBody(struct EightSVXInfo *esvx)
  717.     {
  718.     struct IFFHandle *iff;
  719.     LONG sbytes, rlen, error = 0L; 
  720.     ULONG memtype;
  721.     Voice8Header *vhdr = &esvx->Vhdr;
  722.     BYTE *t;
  723.  
  724.     D(bug("LoadSBody:\n"));
  725.  
  726.     if(!(iff=esvx->ParseInfo.iff))    return(CLIENT_ERROR);
  727.     if(!esvx)                return(CLIENT_ERROR);
  728.  
  729.     if(!(currentchunkis(iff,ID_8SVX,ID_BODY)))
  730.     {
  731.     message("LoadSBody: not at BODY!");
  732.     return(IFFERR_READ);
  733.     }
  734.  
  735.     sbytes  = ChunkMoreBytes(CurrentChunk(iff)); 
  736.  
  737.     /* if we have to decompress, let's just load it into public mem */
  738.     memtype = vhdr->sCompression ? MEMF_PUBLIC : MEMF_CHIP;
  739.  
  740.     D(bug("LoadSBody: samplebytes=%ld, compression=%ld\n",
  741.             sbytes,vhdr->sCompression));
  742.     
  743.     if(!(esvx->sample = (BYTE *)AllocMem(sbytes, memtype))) 
  744.     {
  745.         error = IFFERR_NOMEM;    /* used to be flagged as client error */ 
  746.     }
  747.     else 
  748.     {
  749.     D(bug("LoadSBody: have load buffer\n"));
  750.     esvx->samplebytes = sbytes; 
  751.         if(rlen=ReadChunkBytes(iff,esvx->sample,sbytes) != sbytes)
  752.         error = IFFERR_READ;
  753.  
  754.     if(error)
  755.         {
  756.         D(bug("LoadSBody: ReadChunkBytes error = %ld, read %ld bytes\n",
  757.             error));
  758.         }
  759.     else if (vhdr->sCompression == sCmpFibDelta ) /* Decompress, if needed. */
  760.         {
  761.             if(t = (BYTE *)AllocMem(sbytes<<1, MEMF_CHIP)) 
  762.         {
  763.         D(bug("LoadSBody: have decompression buffer\n"));
  764.                 DUnpack(esvx->sample, sbytes, t); 
  765.                 FreeMem(esvx->sample, sbytes); 
  766.                 esvx->sample = t;
  767.                 esvx->samplebytes = sbytes << 1;
  768.         }
  769.         else
  770.         {
  771.         error = IFFERR_NOMEM;
  772.         }
  773.         } 
  774.     else if (vhdr->sCompression)    /* unknown ompression method */
  775.         {
  776.         error = CLIENT_ERROR;
  777.         }
  778.     }
  779.     if(error)    UnloadSample(esvx);
  780.     return(error);
  781.     } 
  782.  
  783.  
  784. /** UnloadSBody() ********************************************************
  785.  * 
  786.  * Deallocates esvx->smaple  
  787.  * 
  788.  *************************************************************************/
  789. void UnloadSBody(struct EightSVXInfo *esvx)
  790.     {
  791.     if(esvx)
  792.     {
  793.     D(bug("UnloadSBody:\n"));
  794.     if(esvx->sample)
  795.         {
  796.         D(bug("About to free SBody\n"));
  797.         FreeMem(esvx->sample,esvx->samplebytes);
  798.         esvx->sample = NULL;
  799.         }
  800.     esvx->samplebytes = NULL;
  801.     }
  802.     }
  803.  
  804.  
  805. /* DUnpack.c --- Fibonacci Delta decompression by Steve Hayes */
  806.  
  807. /* Fibonacci delta encoding for sound data */
  808. BYTE codeToDelta[16] = {-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};
  809.  
  810. /* Unpack Fibonacci-delta encoded data from n byte source
  811.  * buffer into 2*n byte dest buffer, given initial data
  812.  * value x.  It returns the lats data value x so you can
  813.  * call it several times to incrementally decompress the data.
  814.  */
  815.  
  816. BYTE D1Unpack(BYTE source[], LONG n, BYTE dest[], BYTE x)
  817.    {
  818.    BYTE d;
  819.    LONG i, lim;
  820.  
  821.    lim = n << 1;
  822.    for (i=0; i < lim; ++i)
  823.       {
  824.       /* Decode a data nibble, high nibble then low nibble */
  825.       d = source[i >> 1];    /* get a pair of nibbles */
  826.       if (i & 1)             /* select low or high nibble */
  827.          d &= 0xf;           /* mask to get the low nibble */
  828.       else
  829.          d >>= 4;            /* shift to get the high nibble */
  830.       x += codeToDelta[d];   /* add in the decoded delta */
  831.       dest[i] = x;           /* store a 1 byte sample */
  832.       }
  833.    return(x);
  834.    }
  835.  
  836. /* Unpack Fibonacci-delta encoded data from n byte
  837.  * source buffer into 2*(n-2) byte dest buffer.
  838.  * Source buffer has a pad byte, an 8-bit initial
  839.  * value, followed by n-2 bytes comprising 2*(n-2)
  840.  * 4-bit encoded samples.
  841.  */
  842.  
  843. void DUnpack(source, n, dest)
  844. BYTE source[], dest[];
  845. LONG n;
  846.    {
  847.    D1Unpack(source+2, n-2, dest, source[1]);
  848.    }
  849.  
  850.  
  851. /** LoadSMUS() **********************************************************
  852.  * 
  853.  * Read SMUS, given an initialized SMUSInfo with not-in-use IFFHandle,
  854.  *   and filename.  Leaves the IFFHandle open so you can FindProp()
  855.  *   additional chunks or copychunks().  You must UnloadSMUS()
  856.  *   when done.  UnloadSMUS will closeifile if the file is still
  857.  *   open.
  858.  *
  859.  * Fills in smus->Shdr and Name, and sets up smus traks[],
  860.  *   array setting smus->nevents[] to number of events in each.
  861.  *
  862.  * Returns 0 for success of an IFFERR (libraries/iffparse.h)
  863.  *************************************************************************/
  864. LONG LoadSMUS(struct SMUSInfo *smus, UBYTE *filename)
  865.     {
  866.     struct ContextNode *cn;
  867.     struct IFFHandle *iff;
  868.     struct StoredProperty *sp;
  869.     SScoreHeader *shdr;
  870.     LONG error = 0L, rlen, inslock, oldlock;
  871.     UBYTE insname[80];
  872.  
  873.     D(bug("LoadSMUS:\n"));
  874.     
  875.     if(!smus)                return(CLIENT_ERROR);
  876.     if(!(iff=smus->ParseInfo.iff))    return(CLIENT_ERROR);
  877.  
  878.     if(!(error = openifile((struct ParseInfo *)smus, filename, IFFF_READ)))
  879.     {
  880.     printf("Reading '%s'...\n",filename);
  881.     error = parseifile((struct ParseInfo *)smus,
  882.             ID_FORM, ID_SMUS,
  883.             smus->ParseInfo.propchks,
  884.             smus->ParseInfo.collectchks,
  885.             smus->ParseInfo.stopchks);
  886.  
  887.     D(bug("LoadSMUS: after parseifile - error:%ld, %s\n",error,IFFerr(error)));
  888.  
  889.     if((!error)||(error == IFFERR_EOC)||(error == IFFERR_EOF))
  890.         {
  891.         if(contextis(iff,ID_SMUS,ID_FORM))
  892.         {  /* Is a SMUS */
  893.         D(bug("LoadSMUS: context is SMUS\n"));
  894.         if(!(sp = FindProp(iff,ID_SMUS,ID_SHDR)))
  895.             {
  896.             message("No SMUS.SHDR!");
  897.             error = IFFERR_SYNTAX;
  898.             }
  899.         else
  900.             {
  901.             D(bug("LoadSMUS: Have SHDR\n"));
  902.             /* copy SScoreHeader into frame */
  903.             shdr = (SScoreHeader *)(sp->sp_Data);
  904.             *(&smus->Shdr) = *shdr;
  905.             /* copy name if any */
  906.             smus->name[0]='\0';
  907.             if(sp = FindProp(iff,ID_SMUS,ID_NAME))
  908.             {
  909.             strncpy(smus->name,sp->sp_Data,sp->sp_Size);
  910.             smus->name[MIN(sp->sp_Size,79)] = '\0';
  911.             }
  912.                 while(1)
  913.                     {
  914.                     /* We only asked to stop at SMUS INS1 and TRAK chunks
  915.                      * If no error we've hit a stop chunk
  916.                      */
  917.                     cn = CurrentChunk(iff);
  918.  
  919.                     if((cn)&&(cn->cn_Type == ID_SMUS)&&
  920.                     (cn->cn_ID == ID_INS1))
  921.                             {
  922.                             printf("INS1 chunk found\n");
  923.                 if(esvxs[ei] = (struct EightSVXInfo *)
  924.                 AllocMem(sizeof(struct EightSVXInfo),
  925.                         MEMF_PUBLIC|MEMF_CLEAR))
  926.                 {
  927.                 /* Clone but needs new handle */
  928.                 *(esvxs[ei]) = *esvx;
  929.                     if(!(esvxs[ei]->ParseInfo.iff =
  930.                     AllocIFF())) bye(noiffh,RETURN_FAIL); 
  931.  
  932.                 /* Get name of sound */
  933.                         rlen=ReadChunkBytes(iff,&smus->insflags[ei],4);
  934.                         rlen=ReadChunkBytes(iff,insname,
  935.                       MIN(cn->cn_Size - 4, 79));
  936.  
  937.                 if(rlen != (cn->cn_Size-4)) error = IFFERR_READ;
  938.                 else insname[rlen] = '\0';
  939.  
  940.                 D(bug("Instrument name is %s\n",insname));
  941.                 if(!error)
  942.                     {
  943.                     if(inslock = Lock(inspath,ACCESS_READ))
  944.                     {
  945.                     oldlock = CurrentDir(inslock);
  946.                     error = LoadSample(esvxs[ei],insname);
  947.                     CurrentDir(oldlock);
  948.                     UnLock(inslock);
  949.                     }
  950.                 if (error) printf("Can't load instrument %s\n",
  951.                             insname);
  952.                     }
  953.                 ei++;
  954.                 ecnt++;
  955.                 }
  956.                 }
  957.             else if((cn)&&(cn->cn_Type == ID_SMUS)&&
  958.                     (cn->cn_ID == ID_TRAK))
  959.                 {
  960.                 printf("TRAK chunk found, size=%ld\n",
  961.                     cn->cn_Size);
  962.  
  963.                 if( smus->traks[ti]  = (SEvent *)
  964.                 AllocMem(cn->cn_Size, MEMF_PUBLIC))
  965.                 {
  966.                     smus->tbytes[ti] = cn->cn_Size;
  967.                     if((rlen=ReadChunkBytes(iff,smus->traks[ti],
  968.                       cn->cn_Size)) != cn->cn_Size)
  969.                         error = IFFERR_READ;
  970.                     printf("   Loaded at $%lx\n",smus->traks[ti]);
  971.                 ti++;
  972.                 tcnt++;
  973.                 }
  974.                 else error = IFFERR_NOMEM;
  975.                 }
  976.             if(error) break;
  977.                     else error = ParseIFF(iff,IFFPARSE_SCAN);
  978.                     if(error == IFFERR_EOC) continue;       /* enter next context */
  979.                     else if(error) break;
  980.             }
  981.             }
  982.         }
  983.         else
  984.         {
  985.         message("Not a SMUS\n");
  986.         error = NOFILE;
  987.         }
  988.         }
  989.     }
  990.  
  991.     if(error == IFFERR_EOC) error = 0;
  992.  
  993.     D(bug("LoadSMUS: error = %ld\n",error));
  994.  
  995.     if(error)
  996.     {
  997.     closeifile((struct ParseInfo *)smus);
  998.     UnloadSMUS(smus);
  999.     }
  1000.  
  1001.     return(error);
  1002.     }
  1003.  
  1004.  
  1005. /** UnloadSMUS() *******************************************************
  1006.  * 
  1007.  * Frees and closes everything opened/alloc'd by LoadSMUS()
  1008.  *
  1009.  *************************************************************************/
  1010. void UnloadSMUS(struct SMUSInfo *smus)
  1011.     {
  1012.     int ti;
  1013.  
  1014.     D(bug("In UnloadSMUS\n"));
  1015.     if(smus)
  1016.     {
  1017.     for(ti=0; (ti < MAXTRACKS) && (smus->traks[ti]); ti++)
  1018.         {
  1019.         printf("Freeing %ld at $%lx\n",smus->traks[ti],smus->tbytes[ti]);
  1020.         FreeMem(smus->traks[ti],smus->tbytes[ti]);
  1021.         smus->traks[ti] = NULL;
  1022.         }
  1023.     closeifile((struct ParseInfo *)smus);
  1024.     }
  1025.     }
  1026.  
  1027.